﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Windows;
using System.Windows.Controls;
using BindableDataGrid.Data;
using Combinatorics;
using SilveR.DataValidators;
using SilveR.GUIValidators;

namespace SilveR.Views.StatsViews
{
    public partial class PairedTTest : UserControl, IAnalysis
    {

        private PairedTTestVariables pttVariables;

        public PairedTTest()
        {
            InitializeComponent();

            pttVariables = new PairedTTestVariables();
            this.DataContext = pttVariables;
        }

        private void cboData_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            DataHandler dh = (DataHandler)cboData.SelectedItem;
            pttVariables.AvailableVariables = dh.GetVariableList();
        }

        private void Target_ItemDragCompleted(object sender, ItemDragEventArgs e)
        {
            //ensure that the available variables stay sorted...
            SortableObservableCollection<ColumnInfo> availableVariables = (SortableObservableCollection<ColumnInfo>)lstAvailableVariables.ItemsSource;
            availableVariables.Sort(new ColumnIndexComparer());

            //only enable the covariate transformation dropdown if it has items
            cboResponseTransformations.IsEnabled = !String.IsNullOrEmpty(pttVariables.Response);

            //only enable the covariate transformation dropdown if it has items
            cboCovariateTransformations.IsEnabled = !String.IsNullOrEmpty(pttVariables.Covariate);
        }

        private void oneItem_DragOverOrDrop(object sender, Microsoft.Windows.DragEventArgs e)
        {
            ListBoxDragDropTarget target = (ListBoxDragDropTarget)sender;
            ListBox lstBox = (ListBox)target.Content;

            if (lstBox.Items.Count == 1)
            {
                e.Effects = Microsoft.Windows.DragDropEffects.None;
                e.Handled = true;
            }
        }

        #region IAnalysis Members

        public bool ValidateGUI()
        {
            List<ValidationResult> validationResult = new List<ValidationResult>();
            bool validatedOK = Validator.TryValidateObject(pttVariables, new ValidationContext(pttVariables, null, null), validationResult, true);

            if (!validatedOK)
            {
                ValidationMessagesWindow vmw = new ValidationMessagesWindow(validationResult);
                vmw.Show();
            }

            return validatedOK;
        }

        public event DataValidatedEventHandler DataValidated;

        public void ValidateData()
        {
            PairedTTestValidator npv = new PairedTTestValidator(pttVariables);
            bool result = npv.Validate();

            if (npv.WarningMessages.Count > 0)
            {
                ValidationMessagesWindow vmw = new ValidationMessagesWindow(npv.WarningMessages);
                vmw.Closed += new EventHandler(vmw_Closed);
                vmw.Show();
            }
            else
            {
                if (DataValidated != null)
                    DataValidated(this, new DataValidatedEventArgs(result));
            }
        }

        private void vmw_Closed(object sender, EventArgs e)
        {
            ValidationMessagesWindow vmw = (ValidationMessagesWindow)sender;

            if (DataValidated != null)
                DataValidated(this, new DataValidatedEventArgs(vmw.DialogResult.Value));
        }

        public string[] ExportData()
        {
            DataTable dtNew = this.DataHandler.DataTable.Copy();
            dtNew.Name = "ExportData";

            //Get the response, treatment and covariate columns by removing all other columns from the new datatable
            foreach (DataColumn col in this.DataHandler.DataTable.Columns)
            {
                if (pttVariables.Response != col.ColumnName && !pttVariables.Treatment.Contains(col.ColumnName) && !pttVariables.OtherDesignFactorColumnNames.Contains(col.ColumnName) && pttVariables.Subject != col.ColumnName && pttVariables.Covariate != col.ColumnName)
                {
                    dtNew.Columns.Remove(col);
                }
            }

            //ensure that all data is trimmed
            dtNew.TrimAllDataInDataTable();

            //if the response is blank then remove that row
            dtNew.RemoveBlankRow(pttVariables.Response);

            //Now do transformations...
            dtNew.TransformColumn(pttVariables.Response, pttVariables.ResponseTransformation);

            if (!String.IsNullOrEmpty(pttVariables.Covariate)) //check that a covariate is selected
            {
                dtNew.TransformColumn(pttVariables.Covariate, pttVariables.CovariateTransformation);
            }

            //Finally, as numeric categorical variables get misinterpreted by r, we need to go through
            //each column and put them in quotes...
            foreach (string treat in pttVariables.OtherDesignFactorColumnNames)
            {
                if (dtNew.CheckIsNumeric(treat))
                {
                    foreach (DataRow row in dtNew.Rows)
                    {
                        row[treat] = "'" + row[treat] + "'";
                    }
                }
            }

            return dtNew.GetCSVArray();
        }

        public DataHandler DataHandler
        {
            get { return pttVariables.SelectedDataHandler; }
        }

        public Dictionary<string, string> GetAnalysisArguments()
        {
            Dictionary<string, string> arguments = new Dictionary<string, string>();

            //first thing to do is to assemble the model (use the GetModel method)
            arguments.Add("Model", ArgumentConverters.ConvertIllegalChars(pttVariables.GetModel())); //4

            arguments.Add("Treatment", ArgumentConverters.GetNULLOrText(ArgumentConverters.ConvertIllegalChars(pttVariables.Treatment))); //5

            arguments.Add("Subject", ArgumentConverters.GetNULLOrText(ArgumentConverters.ConvertIllegalChars(pttVariables.Subject))); //6

            //assemble a model for the covariate plot (if a covariate has been chosen)...
            if (String.IsNullOrEmpty(pttVariables.Covariate)) //7
            {
                arguments.Add("Covariate Model", "NULL");
            }
            else
            {
                string covariateModel = pttVariables.Response + "~" + pttVariables.Covariate;
                arguments.Add("Covariate Model", ArgumentConverters.ConvertIllegalChars(covariateModel));
            }

            arguments.Add("Covariance", "\"" + pttVariables.Covariance + "\""); //8

            arguments.Add("Response Transformation", "\"" + pttVariables.ResponseTransformation + "\""); //9

            arguments.Add("Covariate Transformation", "\"" + pttVariables.CovariateTransformation + "\""); //10

            string blocks = null;
            foreach (string s in pttVariables.OtherDesignFactorColumnNames) blocks = blocks + "," + s;

            if (String.IsNullOrEmpty(blocks)) //11
                arguments.Add("Other Factors", "NULL");
            else
                arguments.Add("Other Factors", ArgumentConverters.ConvertIllegalChars(blocks.TrimStart(',')));

            arguments.Add("Produce ANOVA", ArgumentConverters.GetYesOrNo(pttVariables.ANOVASelected)); //12
            arguments.Add("Produce Predicted vs Residuals Plot", ArgumentConverters.GetYesOrNo(pttVariables.PRPlotSelected)); //13
            arguments.Add("Produce Normal Plot", ArgumentConverters.GetYesOrNo(pttVariables.NormalPlotSelected)); //14

            arguments.Add("Significance Level", pttVariables.Significance); //15

            arguments.Add("Produce LS Means", ArgumentConverters.GetYesOrNo(pttVariables.LSMeansSelected)); //16

            return arguments;
        }

        public string AnalysisName
        {
            get { return "Paired t-test"; }
        }

        public string ScriptName
        {
            get { return "PairedTTest.R"; }
        }

        public void UpdateDataHandlers()
        {
            //remember the current data handler before updating the available variables so that it can be selected later...
            DataHandler currentDataHandler = pttVariables.SelectedDataHandler;

            App app = (App)Application.Current;
            pttVariables.AvailableDataHandlers = app.DataHandlers;

            //reselect the current handler (if any)
            pttVariables.SelectedDataHandler = currentDataHandler;
        }

        #endregion
    }

    public class PairedTTestVariables : INotifyPropertyChanged
    {
        public PairedTTestVariables()
        {
            App app = (App)Application.Current;
            availableDataHandlers = app.DataHandlers;
        }

        public string GetModel()
        {
            //assemble the model from the information in the treatment, other factors, response and covariate boxes
            string model = Response + "~";

            if (!String.IsNullOrEmpty(Covariate))
                model = model + Covariate + "+";

            foreach (string s in OtherDesignFactorColumnNames)
                model = model + s + "+";

            if (!String.IsNullOrEmpty(Treatment))
                model = model + "Timezzz+"; //Time model needs zzz adding on the end for R to be able to recognise Time var

            model = model.TrimEnd('+');

            return model;
        }

        private ObservableCollection<DataHandler> availableDataHandlers = new ObservableCollection<DataHandler>();
        public ObservableCollection<DataHandler> AvailableDataHandlers
        {
            get
            {
                return availableDataHandlers;
            }
            set
            {
                availableDataHandlers = value;
                NotifyPropertyChanged("AvailableDataHandlers");
            }
        }

        private DataHandler selectedDataHandler;
        public DataHandler SelectedDataHandler
        {
            get { return selectedDataHandler; }
            set
            {
                selectedDataHandler = value;
                NotifyPropertyChanged("SelectedDataHandler");
            }
        }


        private ObservableCollection<ColumnInfo> availableVariables = new ObservableCollection<ColumnInfo>();
        public ObservableCollection<ColumnInfo> AvailableVariables
        {
            get { return availableVariables; }
            set
            {
                availableVariables = value;
                NotifyPropertyChanged("AvailableVariables");
            }
        }

        private ObservableCollection<ColumnInfo> responseList = new ObservableCollection<ColumnInfo>();
        public ObservableCollection<ColumnInfo> ResponseList
        {
            get { return responseList; }
            set
            {
                responseList[0] = value[0];
                NotifyPropertyChanged("ResponseList");
            }
        }

        [CustomValidation(typeof(BasicGUIValidator), "CheckStringVarSet")]
        [Display(Name = "Response")]
        public string Response
        {
            get
            {
                if (responseList.Count == 1)
                    return responseList[0].ColumnName;
                else
                    return null;
            }
        }

        private ObservableCollection<ColumnInfo> treatmentList = new ObservableCollection<ColumnInfo>();
        public ObservableCollection<ColumnInfo> TreatmentList
        {
            get { return treatmentList; }
            set
            {
                treatmentList[0] = value[0];
                NotifyPropertyChanged("TreatmentList");
            }
        }

        [CustomValidation(typeof(BasicGUIValidator), "CheckStringVarSet")]
        [Display(Name = "Treatment")]
        public string Treatment
        {
            get
            {
                if (treatmentList.Count == 1)
                    return treatmentList[0].ColumnName;
                else
                    return null;
            }
        }

        private ObservableCollection<ColumnInfo> subjectFactorList = new ObservableCollection<ColumnInfo>();
        public ObservableCollection<ColumnInfo> SubjectFactorList
        {
            get { return subjectFactorList; }
            set
            {
                subjectFactorList[0] = value[0];
                NotifyPropertyChanged("SubjectFactorList");
            }
        }

        [CustomValidation(typeof(BasicGUIValidator), "CheckStringVarSet")]
        [Display(Name = "Subject")]
        public string Subject
        {
            get
            {
                if (subjectFactorList.Count == 1)
                    return subjectFactorList[0].ColumnName;
                else
                    return null;
            }
        }

        private ObservableCollection<ColumnInfo> otherDesignFactors = new ObservableCollection<ColumnInfo>();
        public ObservableCollection<ColumnInfo> OtherDesignFactors
        {
            get { return otherDesignFactors; }
            set
            {
                otherDesignFactors = value;
                NotifyPropertyChanged("OtherDesignFactors");
            }
        }

        public List<string> OtherDesignFactorColumnNames
        {
            get
            {
                List<string> names = new List<string>();

                foreach (ColumnInfo ci in otherDesignFactors) names.Add(ci.ColumnName);
                return names;
            }
        }

        private string responseTransformation = "None";
        public string ResponseTransformation
        {
            get { return responseTransformation; }
            set { responseTransformation = value; }
        }

        private List<string> transformationList = new List<string>() { "None", "Log10", "Loge", "Square Root", "ArcSine", "Rank" };
        public List<string> TransformationList
        {
            get { return transformationList; }
        }

        private List<string> covarianceList = new List<string>() { "Compound Symmetric", "Unstructured", "Autoregressive(1)" };
        public List<string> CovarianceList
        {
            get { return covarianceList; }
        }

        private string covariance = "Compound Symmetric";
        public string Covariance
        {
            get { return covariance; }
            set
            {
                covariance = value;
                NotifyPropertyChanged("Covariance");
            }
        }

        private ObservableCollection<ColumnInfo> covariateList = new ObservableCollection<ColumnInfo>();
        public ObservableCollection<ColumnInfo> CovariateList
        {
            get { return covariateList; }
            set
            {
                covariateList[0] = value[0];
                NotifyPropertyChanged("CovariateList");
            }
        }

        public string Covariate
        {
            get
            {
                if (covariateList.Count == 1)
                    return covariateList[0].ColumnName;
                else
                    return null;
            }
        }

        private string covariateTransformation = "None";
        public string CovariateTransformation
        {
            get { return covariateTransformation; }
            set { covariateTransformation = value; }
        }

        private bool anovaSelected = true;
        public bool ANOVASelected
        {
            get { return anovaSelected; }
            set
            {
                anovaSelected = value;
                NotifyPropertyChanged("ANOVASelected");
            }
        }

        private bool prPlotSelected = true;
        public bool PRPlotSelected
        {
            get { return prPlotSelected; }
            set
            {
                prPlotSelected = value;
                NotifyPropertyChanged("PRPlotSelected");
            }
        }

        private bool normalPlotSelected;
        public bool NormalPlotSelected
        {
            get { return normalPlotSelected; }
            set
            {
                normalPlotSelected = value;
                NotifyPropertyChanged("NormalPlotSelected");
            }
        }

        private bool lsMeansSelected;
        public bool LSMeansSelected
        {
            get { return lsMeansSelected; }
            set
            {
                lsMeansSelected = value;
                NotifyPropertyChanged("LSMeansSelected");
            }
        }

        private string significance = "0.05";
        public string Significance
        {
            get { return significance; }
            set { significance = value; }
        }

        private List<string> significanceList = new List<string>() { "0.1", "0.05", "0.01", "0.001" };
        public List<string> SignificanceList
        {
            get { return significanceList; }
        }


        public event PropertyChangedEventHandler PropertyChanged;

        // NotifyPropertyChanged will raise the PropertyChanged event, passing the source property that is being updated.
        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}